/*
 *    Copyright(c)2023-2024
 *    项目名称:hello-world
 *    文件名称:ServerServiceImpl.java
 *    Date:2023/11/18 下午11:45
 *    Author:赵士杰
 */

package com.hw.monitor.service.impl;

import com.hw.common.constant.ServerConstants;
import com.hw.common.utils.date.DateUtils;
import com.hw.common.utils.file.SizeUtils;
import com.hw.common.utils.ip.IpUtils;
import com.hw.common.utils.number.ArithUtils;
import com.hw.monitor.entity.*;
import com.hw.monitor.service.IServerService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.connection.RedisConnection;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import oshi.SystemInfo;
import oshi.hardware.CentralProcessor;
import oshi.hardware.GlobalMemory;
import oshi.hardware.HardwareAbstractionLayer;
import oshi.software.os.FileSystem;
import oshi.software.os.OperatingSystem;
import oshi.util.Util;

import java.lang.management.ManagementFactory;
import java.time.ZoneOffset;
import java.util.List;
import java.util.Objects;
import java.util.Properties;
import java.util.stream.Collectors;

/**
 * @Project hello-world
 * @ClassName MonitorServiceImpl
 * @Description //TODO
 * @Author Jie
 * @Date 2023/11/6 21:30
 */
@SuppressWarnings(value = {"rawtypes" })
@Service
public class ServerServiceImpl implements IServerService {

    private static final int OSHI_WAIT_SECOND = 1000;

    private static final SystemInfo systemInfo = new SystemInfo();

    private static final HardwareAbstractionLayer hal = systemInfo.getHardware();

    private static final OperatingSystem os = systemInfo.getOperatingSystem();

    // 获取文件系统信息
    private static final FileSystem fileSystem = os.getFileSystem();

    @Autowired
    private RedisTemplate redisTemplate;

    @Override
    public Redis getRedisInfo() {

        Redis redis = new Redis();
        RedisConnection connection = Objects.requireNonNull(redisTemplate.getConnectionFactory()).getConnection();

        Properties properties = connection.info();
        if (properties != null) {

            String os = properties.getProperty("os");
            String redisVersion = properties.getProperty("redis_version");
            String gccVersion = properties.getProperty("gcc_version");
            String redisMode = properties.getProperty("redis_mode");
            String tcpPort = properties.getProperty("tcp_port");
            String curConnectedClients = properties.getProperty("connected_clients");
            String hisConnectedClients = properties.getProperty("total_connections_received");

            long uptimeInSeconds = Long.parseLong(properties.getProperty("uptime_in_seconds"));
            String runTime = DateUtils.convertSecondsToDuration(uptimeInSeconds);

            long used_memory = Long.parseLong(properties.getProperty("used_memory"));
            String usedMemory = SizeUtils.byteConvert(used_memory);

            long used_memory_peak = Long.parseLong(properties.getProperty("used_memory_peak"));
            String usedMemoryPeak = SizeUtils.byteConvert(used_memory_peak);

            long used_memory_lua = Long.parseLong(properties.getProperty("used_memory_lua"));
            String usedMemoryLua = SizeUtils.byteConvert(used_memory_lua);

            long used_memory_scripts = Long.parseLong(properties.getProperty("used_memory_scripts"));
            String usedMemoryScripts = SizeUtils.byteConvert(used_memory_scripts);

            String replacePolicy = properties.getProperty("maxmemory_policy");

            String keyTotal = String.valueOf(redisTemplate.keys("*").size());

            String keyMissCount = properties.getProperty("keyspace_misses");
            String keyHitCount = properties.getProperty("keyspace_hits");

            String expiredKeyCount = properties.getProperty("expired_keys");

            long aof_current_size = Long.parseLong(properties.getProperty("aof_current_size"));
            String aofSize = SizeUtils.byteConvert(aof_current_size);

            String aofEnabled = properties.getProperty("aof_enabled");
            String aofLastWriteStatus = properties.getProperty("aof_last_write_status");

            String rdbLastSaveStatus = properties.getProperty("rdb_last_bgsave_status");
            String rdbSaveCount = properties.getProperty("rdb_saves");

            long rdb_last_save_time = Long.parseLong(properties.getProperty("rdb_last_save_time"));
            String rdbLastSaveTime = DateUtils.
                    TimeStampConverter(DateUtils.YYYY_MM_DD_HH_MM_SS, rdb_last_save_time, ZoneOffset.UTC);

            String rdbChangesSinceLastSave = properties.getProperty("rdb_changes_since_last_save");

            long total_net_output_bytes = Long.parseLong(properties.getProperty("total_net_output_bytes"));
            String totalNetOutputSize = SizeUtils.byteConvert(total_net_output_bytes);

            long total_net_input_bytes = Long.parseLong(properties.getProperty("total_net_input_bytes"));
            String totalNetInputSize = SizeUtils.byteConvert(total_net_input_bytes);

            String hisCommandProcessed = properties.getProperty("total_commands_processed");


            redis.setOs(os);
            redis.setVersion(redisVersion);
            redis.setGccVersion(gccVersion);
            redis.setMode(redisMode);
            redis.setPort(tcpPort);
            redis.setCurConnectedClients(curConnectedClients);
            redis.setHisConnectedClients(hisConnectedClients);
            redis.setRunTime(runTime);
            redis.setUsedMemory(usedMemory);
            redis.setUsedMemoryPeak(usedMemoryPeak);
            redis.setUsedMemoryLua(usedMemoryLua);
            redis.setUsedMemoryScripts(usedMemoryScripts);
            redis.setReplacePolicy(replacePolicy);
            redis.setKeyTotal(keyTotal);
            redis.setKeyMissCount(keyMissCount);
            redis.setKeyHitCount(keyHitCount);
            redis.setExpiredKeyCount(expiredKeyCount);
            redis.setAofSize(aofSize);
            redis.setAofEnabled(aofEnabled);
            redis.setAofLastWriteStatus(aofLastWriteStatus);
            redis.setRdbLastSaveStatus(rdbLastSaveStatus);
            redis.setRdbSaveCount(rdbSaveCount);
            redis.setRdbLastSaveTime(rdbLastSaveTime);
            redis.setRdbChangesSinceLastSave(rdbChangesSinceLastSave);
            redis.setTotalNetOutputSize(totalNetOutputSize);
            redis.setTotalNetInputSize(totalNetInputSize);
            redis.setHisCommandProcessed(hisCommandProcessed);

        }

        return redis;
    }

    @Override
    public SystemOs getSystemOsInfo() {
        SystemOs os = new SystemOs();

        Properties props = System.getProperties();
        os.setComputerName(IpUtils.getHostName());
        os.setComputerIp(IpUtils.getHostIp());
        os.setOsName(props.getProperty("os.name"));
        os.setOsArch(props.getProperty("os.arch"));

        return os;
    }

    @Override
    public Memory getMemoryInfo() {
        GlobalMemory globalMemory = hal.getMemory();

        long total = globalMemory.getTotal();
        long available = globalMemory.getAvailable();

        Memory memory = new Memory();
        memory.setTotal(ArithUtils.div(total, (1024 * 1024 * 1024), 2));
        memory.setUsed(ArithUtils.div(total-available, (1024 * 1024 * 1024), 2));
        memory.setAvailable(ArithUtils.div(available, (1024 * 1024 * 1024), 2));
        memory.setUsage(ArithUtils.round( (total-available)*100d/total, 2));

        return memory;
    }

    @Override
    public Jvm getJvmInfo() {
        Jvm jvm = new Jvm();
        Properties props = System.getProperties();

        long total = Runtime.getRuntime().totalMemory();
        long max = Runtime.getRuntime().maxMemory();
        long available = Runtime.getRuntime().freeMemory();

        jvm.setTotal(ArithUtils.div(total, (1024 * 1024), 2));
        jvm.setMax(ArithUtils.div(max, (1024 * 1024), 2));
        jvm.setAvailable(ArithUtils.div(available, (1024 * 1024), 2));
        jvm.setUsed(ArithUtils.div(total-available, (1024 * 1024), 2));
        jvm.setUsage(ArithUtils.div((total - available)*100d, total, 2));

        jvm.setVersion(props.getProperty("java.version"));
        jvm.setHome(props.getProperty("java.home"));
        jvm.setUserDir(props.getProperty("user.dir"));

        jvm.setStartTime(DateUtils.parseDateToStr(DateUtils.YYYY_MM_DD_HH_MM_SS, DateUtils.getServerStartDate()));
        jvm.setRunTime(DateUtils.timeDistance(DateUtils.getNowDate(), DateUtils.getServerStartDate()));
        jvm.setInputArgs(ManagementFactory.getRuntimeMXBean().getInputArguments().toString());

        return jvm;
    }

    @Override
    public List<Disk> getDiskInfo() {
        return fileSystem.getFileStores().stream()
                .map(fs -> {
                    Disk disk = new Disk();
                    disk.setTypeName(fs.getName());
                    long available = fs.getUsableSpace();
                    long total = fs.getTotalSpace();
                    long used = total - available;
                    disk.setDirName(fs.getMount());
                    disk.setSysTypeName(fs.getType());
                    disk.setTypeName(fs.getName());
                    disk.setTotal(SizeUtils.byteConvert(total));
                    disk.setAvailable(SizeUtils.byteConvert(available));
                    disk.setUsed(SizeUtils.byteConvert(used));
                    disk.setUsage(ArithUtils.round(used*100d/total,2));
                    return disk;
                }).collect(Collectors.toList());
    }

    @Override
    public Cpu getCpuInfo() {
        Cpu cpu = new Cpu();

        // 获取CPU信息
        CentralProcessor processor = hal.getProcessor();

        cpu.setName(processor.getProcessorIdentifier().getName());
        cpu.setPhysicalCoreNum(processor.getPhysicalProcessorCount());
        cpu.setLogicalCoreNum(processor.getLogicalProcessorCount());

        long[] prevTicks = processor.getSystemCpuLoadTicks();
        Util.sleep(OSHI_WAIT_SECOND);
        long[] ticks = processor.getSystemCpuLoadTicks();
        long nice = ticks[CentralProcessor.TickType.NICE.getIndex()] - prevTicks[CentralProcessor.TickType.NICE.getIndex()];
        long irq = ticks[CentralProcessor.TickType.IRQ.getIndex()] - prevTicks[CentralProcessor.TickType.IRQ.getIndex()];
        long softIrq = ticks[CentralProcessor.TickType.SOFTIRQ.getIndex()] - prevTicks[CentralProcessor.TickType.SOFTIRQ.getIndex()];
        long steal = ticks[CentralProcessor.TickType.STEAL.getIndex()] - prevTicks[CentralProcessor.TickType.STEAL.getIndex()];
        long system = ticks[CentralProcessor.TickType.SYSTEM.getIndex()] - prevTicks[CentralProcessor.TickType.SYSTEM.getIndex()];
        long user = ticks[CentralProcessor.TickType.USER.getIndex()] - prevTicks[CentralProcessor.TickType.USER.getIndex()];
        long ioWait = ticks[CentralProcessor.TickType.IOWAIT.getIndex()] - prevTicks[CentralProcessor.TickType.IOWAIT.getIndex()];
        long idle = ticks[CentralProcessor.TickType.IDLE.getIndex()] - prevTicks[CentralProcessor.TickType.IDLE.getIndex()];

        long totalCpu = user + nice + system + idle + ioWait + irq + softIrq + steal;

        cpu.setCpuUsage(ArithUtils.round((user + nice + system + irq + softIrq + steal) * 100d / totalCpu,2));
        cpu.setSystemUsage(ArithUtils.round(system * 100d / totalCpu,2));
        cpu.setUserUsage(ArithUtils.round(user * 100d / totalCpu,2));
        cpu.setIdle(ArithUtils.round(idle * 100d / totalCpu,2));
        cpu.setWait(ArithUtils.round(ioWait * 100d / totalCpu,2));

        return cpu;
    }

    @Override
    public Object getServerInfo(String type) {
        switch (type) {
            case ServerConstants.REDIS:
                return getRedisInfo();
            case ServerConstants.CPU:
                return getCpuInfo();
            case ServerConstants.DISK:
                return getDiskInfo();
            case ServerConstants.JVM:
                return getJvmInfo();
            case ServerConstants.MEMORY:
                return getMemoryInfo();
            default:
                return getSystemOsInfo();
        }
    }
}
